#include "../include/storage.h"
#include "../include/cache.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

/**
 * @brief 写入RDB文件头
 * @param fp 文件指针
 * @return 0成功，-1失败
 */
int write_header(FILE* fp)
{
    // 写入魔数“redis”
    // 魔数是Redis的标识，用于识别RDB文件
    // 使用strlen 不包含\0
    if(fwrite(RDB_MAGIC, 1, strlen(RDB_MAGIC), fp) != strlen(RDB_MAGIC))
    {
        return -1;
    }
    // 写入版本号
    if(fwrite(RDB_VERSION, 1, 4, fp) != 4)
    {
        return -1;
    }
    return 0;
}

/**
 * @brief 写入键值对
 * @param fp 文件指针
 * @param node 节点指针
 * @return 0成功，-1失败
 */
int write_key_value(FILE* fp, CacheNode* node)
{
    time_t current_time = time(NULL);
    // 跳过过期节点
    if(node->expire_time > 0 && node->expire_time <= current_time)
    {
        return 0;
    }
    // 写入过期时间
    if(fwrite(&node->expire_time, sizeof(time_t), 1, fp) != 1)
    {
        return -1;
    }
    // 写入键长度和键内容
    int key_len = strlen(node->key);
    if(fwrite(&key_len, sizeof(int), 1, fp) != 1)
    {
        return -1;
    }
    if(fwrite(node->key, 1, key_len, fp) != key_len)
    {
        return -1;
    }
    // 写入值长度和值内容
    int value_len = strlen(node->value);
    if(fwrite(&value_len, sizeof(int), 1, fp) != 1)
    {
        return -1;
    }
    if(fwrite(node->value, 1, value_len, fp) != value_len)
    {
        return -1;
    }
    return 1;
}

/**
 * @brief 保存缓存到RDB文件
 * @param cache 缓存指针
 * @param filename 文件名
 * @return 0成功，-1失败
 */
int rdb_save(Cache* cache, const char* filename)
{
    // 参数验证
    if (cache == NULL || filename == NULL) {
        return -1;
    }

    // 创建临时文件
    char temp_filename[256];
    snprintf(temp_filename, sizeof(temp_filename), "%s.tmp", filename);

    // 打开临时文件
    FILE* temp_file = fopen(temp_filename, "wb");
    if (temp_file == NULL) {
        printf("无法创建临时文件: %s\n", temp_filename);
        perror("fopen错误");
        return -1;
    }
    // 写入文件头
    if(write_header(temp_file) != 0)
    {
        perror("无法写入文件头");
        fclose(temp_file);
        // 删除临时文件
        unlink(temp_filename);
        return -1;
    }
    
    // 统计有效节点
    int valid_nodes = 0;
    // 获取当前文件指针位置，后面会更新
    long count_position = ftell(temp_file);
    // 写入有效节点数
    if(fwrite(&valid_nodes, sizeof(int), 1, temp_file) != 1)
    {
        perror("无法写入有效节点数");
        fclose(temp_file);
        unlink(temp_filename);
        return -1;
    }
    // 直接遍历并写入所有有效节点
    for(int i = 0; i < cache->bucket_count; i++)
    {
        CacheBucket* bucket = &cache->buckets[i];
        pthread_mutex_lock(&bucket->lock);

        CacheNode* current = bucket->head;
        while(current != NULL)
        {
            int ret = write_key_value(temp_file, current);
            if(ret == -1)
            {
                pthread_mutex_unlock(&bucket->lock);
                fclose(temp_file);
                unlink(temp_filename);
                return -1;
            }
            if(ret == 1)
            {
                valid_nodes++;
            }
            current = current->next;
        }
        pthread_mutex_unlock(&bucket->lock);
    }
    // 回到文件开头更新节点数量
    fseek(temp_file, count_position, SEEK_SET);
    if(fwrite(&valid_nodes, sizeof(int), 1, temp_file) != 1)
    {
        perror("无法更新有效节点数");
        fclose(temp_file);
        unlink(temp_filename);
        return -1;
    }
    // 关闭临时文件
    fclose(temp_file);
    // 原子重命名 rename
    if(rename(temp_filename, filename) != 0)
    {
        perror("无法重命名文件");
        unlink(temp_filename);
        return -1;
    }
    printf("RDB保存成功：%s，保存了 %d 个节点\n", filename, valid_nodes);
    return 0;
}

/**
 * @brief 读取键值对
 * @param fp 文件指针
 * @param key 键
 * @param value 值
 * @param expire_time 过期时间
 * @return 1成功，-1失败, 0 过期
 */
int read_key_value(FILE* fp, char** key, char** value, time_t* ttl)
{
    // 读取过期时间
    time_t expire_time;
    // 返回的是读取到元素的个数
    if(fread(&expire_time, sizeof(time_t), 1, fp) != 1)
    {
        return -1;
    }
    
    // 走到这，说明节点有效，将过期时间转换为ttl
    if(expire_time == 0)
    {
        *ttl = 0;
    }else{
        *ttl = expire_time - time(NULL);
    }
    
    // 读取键长度和内容
    int key_len;
    if(fread(&key_len, sizeof(int), 1, fp) != 1)
    {
        return -1;
    }
    *key = (char*)malloc(key_len + 1);
    if(fread(*key, 1, key_len, fp) != key_len)
    {
        free(*key);
        return -1;
    }
    (*key)[key_len] = '\0';
    
    // 读取值长度和内容
    int value_len;
    // 返回的读取到元素个数
    if(fread(&value_len, sizeof(int), 1, fp) != 1)
    {
        free(*key);
        return -1;
    }
    *value = (char*)malloc(value_len + 1);
    if(fread(*value, 1, value_len, fp) != value_len)
    {
        free(*key);
        free(*value);
        return -1;
    }
    (*value)[value_len] = '\0';
    // 成功读取一个节点
    return 1;
}

/**
 * @brief 从RDB文件加载缓存
 * @param cache 缓存指针
 * @param filename 文件名
 * @return 0成功，-1失败
 */
int rdb_load(Cache* cache, const char* filename)
{
    // 参数验证
    if(cache == NULL || filename == NULL)
    {
        return -1;
    }
    // 打开RDB文件
    FILE* fp = fopen(filename, "rb");
    if(fp == NULL)
    {
        perror("无法打开RDB文件");
        return -1;
    }
    // 读取文件头
    char magic[5];
    if(fread(magic, 1, 5, fp) != 5)
    {
        perror("文件头读取失败");
        fclose(fp);
        return -1;
    }
    // 检查魔数
    if(memcmp(magic, RDB_MAGIC, 5) != 0)
    {
        perror("无效的RDB文件");
        fclose(fp);
        return -1;
    }
    // 读取版本号
    char version[4];
    if(fread(version, 1, 4, fp) != 4)
    {
        perror("版本号读取失败");
        fclose(fp);
        return -1;
    }
    // 检查版本号
    if(memcmp(version, RDB_VERSION, 4) != 0)
    {
        perror("不支持的RDB版本");
        fclose(fp);
        return -1;
    }
    // 读取有效节点数
    int valid_nodes;
    if(fread(&valid_nodes, sizeof(int), 1, fp) != 1)
    {
        perror("有效节点数读取失败");
        fclose(fp);
        return -1;
    }
    printf("RDB加载成功：%s，预计加载 %d 个节点\n", filename, valid_nodes);
    // 统计实际加载的节点数
    int loaded_nodes = 0;
    int failed_nodes = 0;
    // 读取所有节点
    for(int i = 0; i < valid_nodes; i++)
    {
        char* key = NULL;
        char* value = NULL;
        time_t ttl = 0;
        // 读取节点数据
        int ret = read_key_value(fp, &key, &value, &ttl);
        if(ret == -1)
        {
            perror("节点读取失败");
            failed_nodes++;
            continue;
        }
        // 直接添加到缓存，不判断是否过期
        if(cache_set(cache, key, value, ttl) == 0)
        {
            loaded_nodes++;
        }
        else
        {
            failed_nodes++;
        }
        free(key);
        free(value);
    }
    fclose(fp);
    printf("RDB加载完成，加载了 %d 个节点，失败 %d 个节点\n", loaded_nodes, failed_nodes);
    return 0;
}

/**
 * @brief 初始化RDB配置
 * @param filename 文件名
 * @param save_interval 保存时间间隔
 * @return RDB配置指针
 */
RDBConfig* rdb_init_config(const char* filename, int save_interval)
{
    RDBConfig* config = (RDBConfig*)malloc(sizeof(RDBConfig));
    if(config == NULL)  return NULL;
    config->filename = strdup(filename);
    if(config->filename == NULL)
    {
        free(config);
        return NULL;
    }
    config->save_interval = save_interval;
    // 默认自动保存
    config->auto_save = 1;
    config->last_save_time = 0;
    return config;
}

/**
 * @brief 销毁RDB配置
 * @param config 配置指针
 */
void rdb_destroy_config(RDBConfig* config)
{
    if(config == NULL)  return;
    free(config->filename);
    free(config);
}

/**
 * @brief 检查是否需要保存,条件 时间 次数
 * @param config 配置指针
 * @param cache 缓存指针
 * @return 1需要保存，0不需要
 */
int rdb_should_save(RDBConfig* config, Cache* cache)
{
    if(config == NULL || cache == NULL)  return 0;
    // 检查是否需要自动保存
    if(config->auto_save == 0)  return 0;

    // 检查保存间隔
    time_t current_time = time(NULL);
    if(current_time - config->last_save_time >= config->save_interval)
    {
        printf("RDB自动保存：%s，距离上次保存 %ld 秒\n", config->filename, current_time - config->last_save_time);        
        return 1;
    }
    // 走到这，说明不需要保存
    // 检查数据变更次数
    pthread_rwlock_rdlock(&cache->stats_lock);
    long long total_operations = cache->stats.hits + cache->stats.misses + cache->stats.evictions + cache->stats.expires;
    pthread_rwlock_unlock(&cache->stats_lock);
    // 检查数据变更次数,如果数据变更次数大于1000，则需要保存
    if(total_operations >= 1000)
    {
        printf("RDB自动保存：%s，数据变更次数 %lld\n", config->filename, total_operations);
        return 1;
    }
    // 走到这，说明不需要保存
    return 0;
}

/**
 * @brief 打印RDB配置信息
 * @param config 配置指针
 */
void rdb_print_info(RDBConfig* config)
{
    if(config == NULL)  return;
    printf("RDB配置信息：\n");
    printf("  文件路径: %s\n", config->filename);
    printf("  保存间隔: %d 秒\n", config->save_interval);
    printf("  自动保存: %s\n", config->auto_save ? "启用" : "禁用");
    printf("  上次保存时间: %ld\n", config->last_save_time);

    time_t current_time = time(NULL);
    printf("  距离上次保存: %ld 秒\n", current_time - config->last_save_time);
}
